package mail.verify.slider;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;

import static com.han.http.util.Print.printlnInfo;

/**
 * @author: Hanjiafeng
 * @date: Created in 14:19 2019/1/29
 * @desc: 图片处理
 */
public class ImageProcessor {

    public static void main(String... args) {
        ImageProcessor imageProcessor = new ImageProcessor();
        BufferedImage image = imageProcessor.readImage("F:\\workspace\\java\\MailCrawler\\src\\main\\resources\\cache\\BaseSlider1551061436005 - 1.png");
        imageProcessor.binarization(image);
        long l = System.currentTimeMillis();
        imageProcessor.writeImage(image, "F:\\workspace\\java\\MailCrawler\\src\\main\\resources\\cache\\b_" + l + ".png");
        String vertex = imageProcessor.vertexSearch(image);
        printlnInfo(vertex);
        imageProcessor.writeImage(image, "F:\\workspace\\java\\MailCrawler\\src\\main\\resources\\cache\\v_" + l + ".png");
    }

    public static int getX(byte[] imageBytes) throws IOException {
        ImageProcessor imageProcessor = new ImageProcessor();

        BufferedImage bufferedImage;
        try {
            bufferedImage = imageProcessor.bytesToBufferedImage(imageBytes);
        } catch (Exception e) {
            throw new IOException("加载滑块背景图片失败");
        }
        String vertex = imageProcessor.getVertex(bufferedImage);
        String[] vertexPoint = vertex.split(",");
        int x = -1;
        try {
            x = Integer.parseInt(vertexPoint[0]);
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
        return x;
    }

    /**
     * 获取图片中最左上角的点
     *
     * @param image 图像路径
     * @return 左上角的顶点, 如果没有返回-1
     */
    private String getVertex(BufferedImage image) {
        //二值化
        binarization(image);
        //获取坐标位置
        return vertexSearch(image);
    }

    /**
     * 二值化处理,将制定阈值以上的像素点置为-1
     *
     * @param image 待处理图片
     */
    private void binarization(BufferedImage image) {
        //读取图片像素等参数
        int width = image.getWidth();
        int height = image.getHeight();
        int[] rgbArray = image.getRGB(0, 0, width, height, null, 0, width);
        //处理
        int red;
        int green;
        int blue;
        int limit1 = 180;
        int limit2 = 100;
        Color tempColor;
        for (int i = 0; i < rgbArray.length; i++) {
            tempColor = new Color(rgbArray[i], true);
            int alpha = tempColor.getAlpha();
            if (alpha != 255) {
                System.out.print(alpha + ",");
            }
            red = tempColor.getRed();
            green = tempColor.getGreen();
            blue = tempColor.getBlue();
            boolean tempBoo = (red > limit1 && green > limit1 && blue > limit2) || (red > limit1 && green > limit2 && blue > limit1) || (red > limit2 && green > limit1 && blue > limit1);
            if (tempBoo) {
                rgbArray[i] = -1;
            } else {
                rgbArray[i] = 0;
            }
        }
        //设置图片的值
        image.setRGB(0, 0, width, height, rgbArray, 0, width);
    }

    /**
     * 获取左上角的顶点坐标
     *
     * @param image 图像
     * @return 左上角坐标, 格式:(x,y),若没有检测到,返回-1
     */
    private String vertexSearch(BufferedImage image) {
        String vertex = "-1";
        //读取图片像素等参数
        int width = image.getWidth();
        int height = image.getHeight();
        int[] linearArray = image.getRGB(0, 0, width, height, null, 0, width);
        //将一维数组转换为二维数组,便于操作
        int[][] dyadicArray = linearToDyadic(linearArray, width);
        ArrayList<Integer> list = new ArrayList<>();
        //识别
        jump:
        for (int h = 0; h < height; h++) {
            for (int w = 0; w < width; w++) {
                if (w <= 20 || h <= 20 || w >= width - 20 || h >= height - 20) {
                    continue;
                }
                //当该点在水平和竖直两个方向都满足条件时,记录点坐标
                if (horizontalRight(dyadicArray, w, h) && verticalRight(dyadicArray, w, h)) {
                    for (int i = 0; i <= 20; i++) {
                        list.add(h * width + w + i);
                        list.add(h * width + w + width * i);
                    }
                    vertex = (w - 65) + "," + h;
                    //获取到左上角的第一个点,跳出两个循环
                    break jump;
                }
            }
        }
        linearArray = dyadicToLinear(dyadicArray);
        for (int i : list) {
            linearArray[i] = new Color(255, 0, 0).getRGB();
        }
        image.setRGB(0, 0, width, height, linearArray, 0, width);

        return vertex;
    }

    /**
     * 判断当前点在水平方向上的像素rgb是否符合要求(y坐标相同,rgb值相同的多数点在15个以上且y-1的点和多数点的rgb相同的不超过5个)
     *
     * @param dyadicArray 二维数组
     * @param x           x
     * @param y           y
     * @return 是否符合要求
     */
    private boolean horizontalRight(int[][] dyadicArray, int x, int y) {
        //初始点y坐标相同的rgb值及对应数量
        Map<Integer, Integer> yMap = new HashMap<>(10);
        //初始点y坐标-1的rgb值及对应数量
        Map<Integer, Integer> yR1Map = new HashMap<>(10);
        int checkoutCount = 20;
        int rgbValue;
        for (int i = 0; i <= checkoutCount; i++) {
            //y坐标相同
            rgbValue = dyadicArray[x + i][y];
            mapCount(yMap, rgbValue);
            //y坐标-1
            rgbValue = dyadicArray[x + i][y - 1];
            mapCount(yR1Map, rgbValue);
        }
        return judgeValue(yMap, yR1Map);
    }

    /**
     * 判断当前点在竖直方向上的像素rgb是否符合要求(x坐标相同,rgb值相同的多数点在15个以上且x-1的点和多数点的rgb相同的不超过5个)
     *
     * @param dyadicArray 二维数组
     * @param x           x
     * @param y           y
     * @return 是否符合要求
     */
    private boolean verticalRight(int[][] dyadicArray, int x, int y) {
        //初始点x坐标相同的rgb值及对应数量
        Map<Integer, Integer> xMap = new HashMap<>(10);
        //初始点x坐标-1的rgb值及对应数量
        Map<Integer, Integer> xR1Map = new HashMap<>(10);
        int checkoutCount = 20;
        int rgbValue;
        for (int i = 0; i <= checkoutCount; i++) {
            //x坐标相同
            rgbValue = dyadicArray[x][y + i];
            mapCount(xMap, rgbValue);
            //x坐标-1
            rgbValue = dyadicArray[x - 1][y + i];
            mapCount(xR1Map, rgbValue);
        }
        return judgeValue(xMap, xR1Map);
    }

    /**
     * 判断map1中的rgbValue最多的点数量是否大于18
     * 判断map1中的rgbValue最多的点的rgbValue在map2中是否超过2
     *
     * @param map1 map1
     * @param map2 map2
     * @return true:map1中最大rgbValue个数超过15其rgbValue在map2中对应的数量不超过2个,否则false
     */
    private boolean judgeValue(Map<Integer, Integer> map1, Map<Integer, Integer> map2) {
        //检测y坐标上的相同rgb值最多点的个数是否大于等于15个
        int yMaxRgbValue = getMaxValue(map1);
        int yMaxCount = map1.get(yMaxRgbValue);
        int yThreshold = 18;
        if (yMaxCount < yThreshold) {
            return false;
        }
        //检测y坐标上相同rgb值对多点的rgb值在y-1坐标上最大个数是否小于5
        int yR1Threshold = 2;
        if (!map2.containsKey(yMaxRgbValue)) {
            return true;
        }
        int yR1RgbCount = map2.get(yMaxRgbValue);
        return yR1RgbCount < yR1Threshold;
    }

    /**
     * 如果map包含rgbValue,对应的rgbCount+1,如果不包含,新增rgbValue对应rgbCount为1
     *
     * @param map      容器
     * @param rgbValue rgbValue
     */
    private void mapCount(Map<Integer, Integer> map, int rgbValue) {
        int rgbCount = 1;
        if (map.containsKey(rgbValue)) {
            rgbCount = map.get(rgbValue);
            rgbCount++;
            map.put(rgbValue, rgbCount);
        } else {
            map.put(rgbValue, rgbCount);
        }
    }

    /**
     * 返回map中rgbValue点最多的键
     *
     * @param map map
     * @return key
     */
    private int getMaxValue(Map<Integer, Integer> map) {
        //检测y坐标上的点是否符合要求
        Set<Integer> keySet = map.keySet();
        int yMaxValue = 0;
        int tempValue;
        int rgbValue = 0;
        for (int key : keySet) {
            tempValue = map.get(key);
            rgbValue = yMaxValue < tempValue ? key : rgbValue;
        }
        return rgbValue;
    }

    /**
     * 一维数组转二维数组
     *
     * @param linearArray 一维数组
     * @param width       二维数组的宽
     * @return 二维数组
     */
    private int[][] linearToDyadic(int[] linearArray, int width) {
        int h;
        int w;
        int[][] dyadicArray = new int[width][linearArray.length / width];
        for (int i = 0; i < linearArray.length; i++) {
            w = i % width;
            h = i / width;
            dyadicArray[w][h] = linearArray[i];
        }
        return dyadicArray;
    }

    /**
     * 二维数组转一维数组
     *
     * @param dyadicArray 二维数组
     * @return 一维数组
     */
    private int[] dyadicToLinear(int[][] dyadicArray) {
        int width = dyadicArray.length;
        int height = dyadicArray[0].length;
        int[] linearArray = new int[width * height];
        for (int h = 0; h < height; h++) {
            for (int w = 0; w < width; w++) {
                int p = h * width + w;
                linearArray[p] = dyadicArray[w][h];
            }
        }

        return linearArray;
    }

    /**
     * 读取图片为BufferedImage
     *
     * @param imagePath 图片路径
     * @return BufferedImage
     */
    private BufferedImage readImage(String imagePath) {
        BufferedImage image = null;
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(imagePath);
            image = ImageIO.read(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                Objects.requireNonNull(inputStream).close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return image;
    }

    /**
     * 将BufferedImage写入指定路径
     *
     * @param image     BufferedImage
     * @param imagePath 路径
     */
    private void writeImage(BufferedImage image, String imagePath) {
        OutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(imagePath);
            ImageIO.write(image, "png", outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                Objects.requireNonNull(outputStream).close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private BufferedImage bytesToBufferedImage(byte[] imageBytes) throws IOException {
        BufferedImage bufferedImage;
        //自动关闭输入流
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(imageBytes)) {
            bufferedImage = ImageIO.read(byteArrayInputStream);
        }

        return bufferedImage;
    }
}
